/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package org.is.city.routemanager;
/*
 *    GeoTools - The Open Source Java GIS Toolkit
 *    http://geotools.org
 *
 *    (C) 2002-2008, Open Source Geospatial Foundation (OSGeo)
 *
 *    This library is free software; you can redistribute it and/or
 *    modify it under the terms of the GNU Lesser General Public
 *    License as published by the Free Software Foundation;
 *    version 2.1 of the License.
 *
 *    This library is distributed in the hope that it will be useful,
 *    but WITHOUT ANY WARRANTY; without even the implied warranty of
 *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 *    Lesser General Public License for more details.
 */

import java.awt.BorderLayout;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.io.IOException;
import java.util.Iterator;
import java.util.List;
import java.util.ResourceBundle;
import java.util.logging.Level;
import java.util.logging.Logger;

import javax.swing.BorderFactory;
import javax.swing.Icon;
import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JToggleButton;
import javax.swing.event.ListDataEvent;
import javax.swing.event.ListDataListener;
import net.miginfocom.swing.MigLayout;
import org.geotools.data.DataSourceException;

import org.geotools.data.DataUtilities;

import org.geotools.geometry.DirectPosition2D;
import org.geotools.geosearch.Guide;
import org.geotools.map.FeatureLayer;
import org.geotools.map.MapContent;
import org.geotools.map.MapContext;


import org.geotools.map.MapLayer;
import org.geotools.map.event.MapLayerListEvent;
import org.geotools.map.event.MapLayerListListener;
import org.geotools.styling.Style;
import org.geotools.swing.JMapPane;
import org.geotools.swing.MapLayerTable;
import org.geotools.swing.control.DnDList;
import org.geotools.swing.control.DnDListModel;
import org.geotools.swing.styling.JSimpleStyleDialog;
import org.is.city.pointsmanager.POImanager;

import org.is.city.routemanager.GUI.MapLayerTableCellRenderer;
import org.is.city.routemanager.GUI.ModuleSettingsFrame;
import org.is.city.routemanager.Layers.PointsLayer;
import org.is.city.routemanager.Layers.RouteLayer;
import org.opengis.feature.simple.SimpleFeatureType;

/**
 * Displays a list of the map layers in an associated {@linkplain JMapPane} and
 * provides controls to set the visibility, selection and style of each layer.
 * <p>
 * Implementation note: DefaultMapContext stores its list of MapLayer objects
 * in rendering order, ie. the layer at index 0 is rendererd first, followed by
 * index 1 etc. MapLayerTable stores its layers in the reverse order since it
 * is more intuitive for the user to think of a layer being 'on top' of other
 * layers.
 *
 * @author Michael Bedward
 * @since 2.6
 * @source $URL: http://svn.osgeo.org/geotools/trunk/modules/unsupported/swing/src/main/java/org/geotools/swing/MapLayerTable.java $
 * @version $Id: MapLayerTable.java 35310 2010-04-30 10:32:15Z jive $
 */
public class RoutePanel extends MapLayerTable implements MapLayerListListener {
    //private static final long serialVersionUID = -8461593609237317561L;

    private static final ResourceBundle stringRes = ResourceBundle.getBundle("META-INF/Text");
    private RoutePanel panel;
    private JMapPane pane;
    private DnDListModel<Route> listModel;
    private DnDList<Route> list;
    private JScrollPane scrollPane;
    POImanager poimanager;

    /* For detecting mouse double-clicks */
    private static final long DOUBLE_CLICK_TIME = 500;
    private long lastClickTime = 0;
    private Guide guide;

    /*
     * Whether to prompt for confirmation before removing a layer.
     * @todo introduce a setter or property for this
     */
    private boolean confirmRemove = true;

    /**
     * Default constructor. A subsequent call to {@linkplain #setMapPane}
     * will be required.
     */
    public RoutePanel() {
        init();
    }
    //!Внимание! переделать
    MapLayer guidanceLayer = null;

    public RoutePanel(JMapPane mapPane, POImanager pOImanager) {
        this.pane = mapPane;
        init();
        setMapPane(pane);
        this.poimanager = pOImanager;

    }

    public MapLayer getGuidanceLayer() {
        return guidanceLayer;
    }

    public void setGuidanceLayer(MapLayer guidanceLayer) {
        this.guidanceLayer = guidanceLayer;
        guide = new Guide(DataUtilities.simple(this.getGuidanceLayer().getFeatureSource()), pane.getMapContext().getCoordinateReferenceSystem(), null, 0);
        System.out.println(guidanceLayer.getFeatureSource().getName().getLocalPart() + " установлено в качестве слоя маршрутизации по умолчанию");
    }

    /**
     * Set the map pane that this MapLayerTable will service.
     *
     * @param pane the map pane
     */
    @Override
    public void setMapPane(JMapPane pane) {
        this.pane = pane;
        //pane.setMapLayerTable(this);
        pane.getMapContext().addMapLayerListListener(this);
    }
    int added_layers = 0; //
    MapLayer tempRouteLayer;
    MapLayer tempPointsLayer;
    Route route_adding_temp=null;

    /**
     * Add a new layer to those listed in the table. This method will be called
     * by the associated map pane automatically as part of the event sequence
     * when a new MapLayer is added to the pane's MapContext.
     *
     * @param layer the map layer
     */
    @Override
    public void onAddLayer(MapLayer layer) {

        if (layer.toLayer() instanceof RouteLayer) {
            if(route_adding_temp.getRouteLayer().equals(layer.toLayer()))
            {
                added_layers++;
                tempRouteLayer = layer;
            }
            
        }
        if (layer.toLayer() instanceof PointsLayer) {
            if(route_adding_temp.getPointsLayer().equals(layer.toLayer())){
                added_layers++;
                tempPointsLayer = layer;
            }
        }
        if (added_layers == 2) {
            added_layers = 0;
            listModel.insertItem(0, route_adding_temp);
        }
        if (layer.getFeatureSource().getSchema().getGeometryDescriptor().getType().getName().toString().equals("MultiLineString")) {
            if (getGuidanceLayer() == null) {
                setGuidanceLayer(layer);
            }

        }
    }

    /**
     * Remove a layer from those listed in the table. This method will be called
     * by the associated map pane automatically as part of the event sequence
     * when a new MapLayer is removed from the pane's MapContext.
     *
     * @param layer the map layer
     */
    void onRemoveLayer(MapLayer layer) {
        //MapContext не должно вызывать этот метод и вмешиваться. поэтому и коммент
        //listModel.removeItem(layer);
    }

    /**
     * Repaint the list item associated with the specified MapLayer object
     *
     * @param layer the map layer
     */
    @Override
    public void repaint(MapLayer layer) {
        /* временно без такой перересовки
        int index = listModel.indexOf(layer);
        list.repaint(list.getCellBounds(index, index));
         * 
         */
    }

    /**
     * Called by the constructor. This method lays out the components that
     * make up the MapLayerTable and registers a mouse listener.
     */
    private void init() {
        this.panel = this;
        listModel = new DnDListModel<Route>();
        pane.addMouseListener(new GuideEditorMapPaneListener(listModel, pane));
        list = new DnDList<Route>(listModel) {

            private static final long serialVersionUID = 1289744440656016412L;
            /*
             * We override setToolTipText to provide tool tips
             * for the control labels displayed for each list item
             */

            @Override
            public String getToolTipText(MouseEvent e) {
                int item = list.locationToIndex(e.getPoint());

                if (item >= 0) {
                    Rectangle r = list.getCellBounds(item, item);
                    if (r.contains(e.getPoint())) {
                        Point p = new Point(e.getPoint().x, e.getPoint().y - r.y);

                        if (MapLayerTableCellRenderer.hitSelectionLabel(p)) {
                            return stringRes.getString("select_layer");

                        } else if (MapLayerTableCellRenderer.hitVisibilityLabel(p)) {
                            return stringRes.getString("show_layer");

                        } else if (MapLayerTableCellRenderer.hitStyleLabel(p)) {
                            return stringRes.getString("style_layer");

                        } else if (MapLayerTableCellRenderer.hitRemoveLabel(p)) {
                            return stringRes.getString("remove_route");

                        } else if (MapLayerTableCellRenderer.hitNameLabel(p)) {
                            return stringRes.getString("rename_layer");
                        }
                    }
                }

                return null;
            }
        };

        // Listen for drag-reordering of the list contents which
        // will be received via the contentsChanged method
        listModel.addListDataListener(new ListDataListener() {

            public void intervalAdded(ListDataEvent e) {
            }

            public void intervalRemoved(ListDataEvent e) {
            }

            public void contentsChanged(ListDataEvent e) {
                onReorderLayers(e);
            }
        });

        list.setCellRenderer(new MapLayerTableCellRenderer());
        list.setFixedCellHeight(MapLayerTableCellRenderer.getCellHeight());

        list.addMouseListener(new MouseAdapter() {

            @Override
            public void mouseClicked(MouseEvent e) {
                long clickTime = System.currentTimeMillis();
                boolean doubleClick = clickTime - lastClickTime < DOUBLE_CLICK_TIME;
                lastClickTime = clickTime;
                onLayerItemClicked(e, doubleClick, pane, panel);
            }
        });

        scrollPane = new JScrollPane(list,
                JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED,
                JScrollPane.HORIZONTAL_SCROLLBAR_ALWAYS);

        scrollPane.setBorder(BorderFactory.createTitledBorder(stringRes.getString("route_panel_title")));

        JPanel btnPanel = new JPanel(new MigLayout());
        Icon showIcon = MapLayerTableCellRenderer.LayerControlItem.VISIBLE.getIcon();
        JButton showAllBtn = new JButton(showIcon);
        showAllBtn.setToolTipText(stringRes.getString("show_all_routes"));
        showAllBtn.addActionListener(new ActionListener() {

            public void actionPerformed(ActionEvent e) {
                onShowAllRoutes();
            }
        });
        btnPanel.add(showAllBtn);

        Icon hideIcon = MapLayerTableCellRenderer.LayerControlItem.VISIBLE.getOffIcon();
        JButton hideAllBtn = new JButton(hideIcon);
        hideAllBtn.setToolTipText(stringRes.getString("hide_all_routes"));
        hideAllBtn.addActionListener(new ActionListener() {

            public void actionPerformed(ActionEvent e) {
                onHideAllRoutes();
            }
        });
        btnPanel.add(hideAllBtn);

        Icon onIcon = new ImageIcon(RoutePanel.class.getResource("/Settings-icon-mini.png"));
        JButton selAllBtn = new JButton(onIcon);
        selAllBtn.setToolTipText(stringRes.getString("route_panel_route_properties"));
        selAllBtn.addActionListener(new ActionListener() {

            public void actionPerformed(ActionEvent e) {
                ModuleSettingsFrame frame = new ModuleSettingsFrame(panel);
                frame.setVisible(true);
            }
        });

        btnPanel.add(selAllBtn);

        Icon offIcon = new ImageIcon(RoutePanel.class.getResource("/guide.png"));
        JToggleButton unselAllBtn = new JToggleButton(offIcon);
        unselAllBtn.setToolTipText(stringRes.getString("route_panel_new_points"));

        unselAllBtn.addActionListener(new MultiPointGuideAction(unselAllBtn, panel, pane, "2"));
        btnPanel.add(unselAllBtn, "wrap");
        Icon guidenIcon = new ImageIcon(RoutePanel.class.getResource("/guiden.png"));
        JToggleButton guidnBtn = new JToggleButton(guidenIcon);
        guidnBtn.addActionListener(new MultiPointGuideAction(guidnBtn, panel, pane, "n"));
        btnPanel.add(guidnBtn);

        Icon saveIcon = new ImageIcon(RoutePanel.class.getResource("/save.png"));
        JButton saveBtn = new JButton(saveIcon);
        saveBtn.addActionListener(new SaveRouteAction(list));
        btnPanel.add(saveBtn);

        Icon openIcon = new ImageIcon(RoutePanel.class.getResource("/open.png"));
        JButton openBtn = new JButton(openIcon);
        openBtn.addActionListener(new OpenRouteAction(pane));
        btnPanel.add(openBtn);

        Icon addIcon = new ImageIcon(POImanager.class.getResource("/add.png"));
        JButton btnCreateNewRoute = new JButton(addIcon);
        btnCreateNewRoute.addActionListener(new ActionListener() {

            @Override
            public void actionPerformed(ActionEvent e) {
                DirectPosition2D array[] = new DirectPosition2D[0];
                addRoute(array,AbstractRouteFactory.OrderType.NOTORDERED);
            }
        });
        btnCreateNewRoute.setToolTipText("Создать новый маршрут");
        btnPanel.add(btnCreateNewRoute);


        setLayout(new BorderLayout());
        add(scrollPane, BorderLayout.CENTER);
        add(btnPanel, BorderLayout.SOUTH);
    }
    //Выделенный маршрут
    Route selectedRoute;

    public Route getSelectedRoute() {
        return selectedRoute;
    }

    /**
     * Handle a mouse click on a cell in the JList that displays
     * layer names and states.
     *
     * @param ev the mouse event
     * @param doubleClick true if this is the second click of a double-click; false otherwise
     */
    private void onLayerItemClicked(MouseEvent ev, boolean doubleClick, JMapPane pane, RoutePanel routePanel) {
        int item = list.locationToIndex(ev.getPoint());

        if (item >= 0) {
            Rectangle r = list.getCellBounds(item, item);
            if (r.contains(ev.getPoint())) {
                Route route = listModel.getElementAt(item);
                RouteLayer routeLayer = route.getRouteLayer();
                PointsLayer pointsLayer = route.getPointsLayer();
                Point p = new Point(ev.getPoint().x, ev.getPoint().y - r.y);

                if (MapLayerTableCellRenderer.hitSelectionLabel(p)) {
                    //maplayerRoute.setSelected(!maplayerRoute.isSelected());
                    //maplayerPoints.setSelected(!maplayerPoints.isSelected());
                    RoutePropertiesFrame properties = new RoutePropertiesFrame(route);
                    properties.setVisible(true);

                } else if (MapLayerTableCellRenderer.hitVisibilityLabel(p)) {
                    routeLayer.setVisible(!routeLayer.isVisible());
                    pointsLayer.setVisible(!pointsLayer.isVisible());

                } else if (MapLayerTableCellRenderer.hitStyleLabel(p)) {
                    doSetStyle(routeLayer);

                } else if (MapLayerTableCellRenderer.hitRemoveLabel(p)) {
                    doRemoveRouteWithConfirm(route);

                } else if (MapLayerTableCellRenderer.hitNameLabel(p)) {
                    if (doubleClick) {
                        doSetRouteName(route);
                    }
                }
                selectedRoute = route;
                poimanager.onRouteLayerSelected(route);
                /*
                 * Хотел сделать перерисовку только определенного участка полотна,
                 * но не прокатило. (работает, но происходит еще перерисовка из
                 * другого места, поэтому плохой вариант
                try {
                ReferencedEnvelope bounds = route.getRoute().getFeatureSource().getBounds();
                AffineTransform worldToScreenTransform = pane.getWorldToScreenTransform();

                DirectPosition2D max=new DirectPosition2D();
                worldToScreenTransform.transform(new DirectPosition2D(bounds.getMaxX(), bounds.getMaxY()), max);
                DirectPosition2D min=new DirectPosition2D();
                worldToScreenTransform.transform(new DirectPosition2D(bounds.getMinX(), bounds.getMinY()), min);
                pane.repaint((int)min.x, (int)max.y, (int)(Math.abs(max.x-min.x)), (int)(Math.abs(max.y-min.y)));
                } catch (IOException ex) {
                Logger.getLogger(RoutePanel.class.getName()).log(Level.SEVERE, null, ex);
                }
                 *
                 */
                pane.repaint();
                routePanel.repaint();


            }
        }
    }

    /**
     * Show a style dialog to create a new Style for the layer
     *
     * @param layer the layer to be styled
     */
    private void doSetStyle(FeatureLayer layer) {
        try {
            SimpleFeatureType simple = DataUtilities.simple(layer.getFeatureSource().getFeatures().getSchema());
            Style style = JSimpleStyleDialog.showDialog(this, simple);
            if (style != null) {
                layer.setStyle(style);
            }
        } catch (IOException ex) {
            Logger.getLogger(RoutePanel.class.getName()).log(Level.SEVERE, null, ex);
        }
    }

    /**
     * Prompt for a new title for the layer
     *
     * @param layer the layer to be renamed
     */
    private void doSetRouteName(Route route) {
        String name = JOptionPane.showInputDialog(stringRes.getString("new_layer_name_message"));
        if (name != null && name.trim().length() > 0) {
            route.setRouteTitle(name.trim());
        }
    }

    /**
     * Called when the user has clicked on the remove layer item.
     *
     * @param layer the layer to remove
     */
    private void doRemoveRouteWithConfirm(Route route) {
        if (confirmRemove) {
            int confirm = JOptionPane.showConfirmDialog(null,
                    stringRes.getString("confirm_remove_route_message"),
                    stringRes.getString("confirm_remove_route_title"),
                    JOptionPane.YES_NO_OPTION);

            if (confirm != JOptionPane.YES_OPTION) {
                return;
            }
        }

        pane.getMapContext().removeLayer(route.getPointsLayer());
        pane.getMapContext().removeLayer(route.getRouteLayer());
        listModel.removeItem(route);
    }

    /**
     * Удаление маршрута без подтверждения
     * @param route
     */
    public void doRemoveRoute(Route route){
        if(pane.getMapContext().removeLayer(route.getPointsLayer()) && pane.getMapContext().removeLayer(route.getRouteLayer()))
            System.out.println("fs");
        listModel.removeItem(route);
    }

    /**
     * Handle a ListDataEvent signallying a drag-reordering of the map layers.
     * The event is published by the list model after the layers have been
     * reordered there.
     *
     * @param ev the event
     */
    private void onReorderLayers(ListDataEvent ev) {
        pane.setRepaint(false);
        /*
        for (int pos = ev.getIndex0(); pos <= ev.getIndex1(); pos++) {
            RouteLayer layer = listModel.getElementAt(pos).getRouteLayer();

            int newContextPos = listModel.getSize() - pos - 1;
MapContent content=pane.getMapContext();
content.
            int curContextPos =  pane.getMapContext(). .indexOf(layer);

            if (curContextPos != newContextPos) {
                pane.getMapContext().moveLayer(curContextPos, newContextPos);
            }
        }
        pane.setRepaint(true);
        pane.repaint();
        */
    }

    private void onShowAllRoutes() {
        if (pane != null && pane.getMapContext() != null) {
            for (int i = 0; i < listModel.getSize(); ++i) {
                listModel.getElementAt(i).getPointsLayer().setVisible(true);
                listModel.getElementAt(i).getRouteLayer().setVisible(true);
            }
        }
    }

    private void onHideAllRoutes() {
        if (pane != null && pane.getMapContext() != null) {
            for (int i = 0; i < listModel.getSize(); ++i) {
                listModel.getElementAt(i).getPointsLayer().setVisible(false);
                listModel.getElementAt(i).getRouteLayer().setVisible(false);
            }
        }
    }

    private void onSelectAllLayers() {
        if (pane != null && pane.getMapContext() != null) {
            for (MapLayer layer : pane.getMapContext().getLayers()) {
                if (!layer.isSelected()) {
                    layer.setSelected(true);
                }
            }
        }
    }

    private void onUnselectAllLayers() {
        if (pane != null && pane.getMapContext() != null) {
            for (MapLayer layer : pane.getMapContext().getLayers()) {
                if (layer.isSelected()) {
                    layer.setSelected(false);
                }
            }
        }
    }

    @Override
    public void layerAdded(MapLayerListEvent event) {
        onAddLayer(event.getLayer());
    }

    @Override
    public void layerRemoved(MapLayerListEvent event) {
        onRemoveLayer(event.getLayer());
    }

    @Override
    public void layerChanged(MapLayerListEvent event) {
    }

    @Override
    public void layerMoved(MapLayerListEvent event) {
    }

    /**
     * Создание и добавление слоев маршрутизации по точкам назначения (destinations)
     * @param points точки назначения
     */
    public void addRoute(DirectPosition2D points[], AbstractRouteFactory.OrderType type) {
        if (guide == null) {
            guide = new Guide(DataUtilities.simple(this.getGuidanceLayer().getFeatureSource()), pane.getMapContext().getCoordinateReferenceSystem(), null, 0);
        }
        if (points.length==0) {
            DefaultRouteFactory factory = new DefaultRouteFactory(guide);
            Route route = factory.createRoute(points,type);
            if (route == null) {
                System.out.println("error; path haven't been found");
                return;
            }
            addRoute(route);
            list.setSelectedIndex(0);
            poimanager.onRouteLayerSelected(route);
            return;
        }
        List selectedValuesList = list.getSelectedValuesList();
        if (selectedValuesList.size() == 1) {
            Iterator iterator = selectedValuesList.iterator();
            Route next = (Route) iterator.next();
            DirectPosition2D[] points1 = next.getPointsPositions();
            DirectPosition2D[] buffer = new DirectPosition2D[points.length + points1.length];
            System.arraycopy(points, 0, buffer, 0, points.length);
            System.arraycopy(points1, 0, buffer, points.length, points1.length);
            points = buffer;
            doRemoveRoute(next);
            listModel.removeItem(next);
        }
        DefaultRouteFactory factory = new DefaultRouteFactory(guide);
        Route route = factory.createRoute(points,type);
        if (route == null) {
            System.out.println("error; path haven't been found");
            return;
        }
        addRoute(route);
        //pane.getMapContext().addLayer(route.getRouteLayer());
        pane.getMapContext().addLayer(route.getPointsLayer());
        list.setSelectedIndex(0);
        
    }

    public void addRoute(Route route){
        if (route==null)
            return;
        MapContext mapContext = pane.getMapContext();
        route_adding_temp=route;
        mapContext.addLayer(route.getRouteLayer());
        mapContext.addLayer(route.getPointsLayer());
        poimanager.onRouteLayerSelected(route);
        //mapContext.removeLayer(route.getPointsLayer());
        //mapContext.removeLayer(route.getRouteLayer());
    }
    public JMapPane getPane() {
        return pane;
    }
}
